package informationretrieval.readers;

import informationretrieval.DocsDatabase;
import informationretrieval.Log;
import informationretrieval.ResultSet;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.apache.lucene.queryparser.flexible.standard.QueryParserUtil;

public class ParameterFileReader {

	// Class variables
	private final static String TAG = ParameterFileReader.class.getSimpleName();
	private String mFileName;
	private String mOutputFileName;
	private String mDocsDatabaseFileName;
	private String mQuerysFileName;
	private String mTruthFileName;
	private Retrieval_Algorithm mRetrievalAlgorithm;
	private ResultSet mTruthResultRank;
	private HashMap<String, String> mDocsList;
	private DocsDatabase mDocsDatabase;
	private HashMap<Integer, String> mQueryMap;
	private List<String> mStopWordsList;
	private HashMap<Parameter, String> mParamteresMap;

	/**
	 * Reads a file by name
	 * 
	 * @param fileName
	 *            The name of the file
	 */
	public ParameterFileReader(String fileName) {
		mFileName = fileName;
		mDocsList = new HashMap<String, String>();
		mQueryMap = new HashMap<Integer, String>();
		mParamteresMap = new HashMap<Parameter, String>();

		try {
			// First read the stop words file and keep it as a list
			mStopWordsList = new ArrayList<String>();
			readDataFile(Parameter.STOP_WORDS_LIST,
					Parameter.STOP_WORDS_LIST.getName());
			mParamteresMap.put(Parameter.STOP_WORDS_LIST,
					Parameter.STOP_WORDS_LIST.getName());

			BufferedReader fileInputBuffer = new BufferedReader(new FileReader(
					mFileName));
			String line = fileInputBuffer.readLine();

			while (line != null) {
				// In case of a comment in the parameter file skip the line
				if (line.startsWith("//") || line.trim().equals("")) {
					line = fileInputBuffer.readLine();
					continue;
				}

				String[] formattedLine = line.split("=");
				String key = formattedLine[0].trim();
				String value = formattedLine[1].trim();

				if (key.equals(Parameter.IR_ALGORITHM.getName())) {
					mRetrievalAlgorithm = Retrieval_Algorithm.valueOf(value
							.toUpperCase());
					mParamteresMap.put(Parameter.IR_ALGORITHM, value);
				}

				if (key.equals(Parameter.OUTPUT_FILE.getName())) {
					mOutputFileName = value;
					mParamteresMap.put(Parameter.OUTPUT_FILE, value);
				}

				if (key.equals(Parameter.DOCS_DATABASE.getName())) {
					mDocsDatabaseFileName = value;
					readDataFile(Parameter.DOCS_DATABASE, value);
					mParamteresMap.put(Parameter.DOCS_DATABASE, value);
				}

				if (key.equals(Parameter.QUERY_LIST.getName())) {
					mQuerysFileName = value;
					readDataFile(Parameter.QUERY_LIST, value);
					mParamteresMap.put(Parameter.QUERY_LIST, value);
				}

				if (key.equals(Parameter.TRUTH_RANK.getName())) {
					mTruthFileName = value;
					getTrueRank(value);
					mParamteresMap.put(Parameter.TRUTH_RANK, value);
				}

				line = fileInputBuffer.readLine();
			}

		} catch (Exception exception) {
			Log.error(TAG, exception, true);
		}
	}

	/**
	 * A method to get parameters from the parameters file using the parameter
	 * key
	 * 
	 * @param parameter
	 *            the parameter key
	 * @return The requested parameter by key
	 */
	public <T> T getParameter(Parameter parameter) {
		// Method variables
		T result = null;

		if (parameter.equals(Parameter.DOCS_DATABASE)) {
			boolean usingImprovedAlgorithm = mRetrievalAlgorithm
					.equals(Retrieval_Algorithm.IMPROVED) ? true : false;
			mDocsDatabase = new DocsDatabase(mStopWordsList,
					usingImprovedAlgorithm);
			mDocsDatabase.indexDocList(mDocsList);
			result = (T) mDocsDatabase;
		}

		if (parameter.equals(Parameter.IR_ALGORITHM)) {
			result = (T) mRetrievalAlgorithm;
		}

		if (parameter.equals(Parameter.OUTPUT_FILE)) {
			result = (T) mOutputFileName;
		}

		if (parameter.equals(Parameter.QUERY_LIST)) {
			result = (T) mQueryMap;
		}

		if (parameter.equals(Parameter.STOP_WORDS_LIST)) {
			result = (T) mStopWordsList;
		}

		if (parameter.equals(Parameter.TRUTH_RANK)) {
			result = (T) mTruthResultRank;
		}

		return result;
	}

	/**
	 * Reads a data file into the suitable data structure
	 * 
	 * @param parameter
	 *            The parameter by which the data will be read
	 * @param fileName
	 *            The file to read
	 */
	private void readDataFile(Parameter parameter, String fileName) {
		try {
			BufferedReader fileInputBuffer = new BufferedReader(new FileReader(
					fileName));
			String line = fileInputBuffer.readLine();

			while (line != null) {
				// In case of a comment in the parameter file skip the line
				if (line.startsWith("//") || line.trim().equals("")) {
					line = fileInputBuffer.readLine();
					continue;
				}

				if (parameter.equals(Parameter.STOP_WORDS_LIST)) {
					mStopWordsList.add(line);
				} else {
					String[] formattedLine = line.split(" ", 2);
					String stringId = formattedLine[0].trim();
					String data = formattedLine[1].trim();
					if (parameter.equals(Parameter.DOCS_DATABASE)) {
						mDocsList.put(stringId, data);
					} else if (parameter.equals(Parameter.QUERY_LIST)) {
						String query = QueryParserUtil.escape(data);
						Integer id = Integer.parseInt(stringId);
						mQueryMap.put(id, query);
					}
				}

				line = fileInputBuffer.readLine();
			}
		} catch (Exception exception) {
			Log.error(TAG, exception, true);
		}
	}

	/**
	 * A method to check if a certain parameter has been instantiated
	 * 
	 * @param parameter
	 *            The parameter to check
	 * @return True in case the parameter has been instantiated false otherwise
	 */
	public boolean containsParameter(Parameter parameter) {
		return mParamteresMap.containsKey(parameter);
	}

	/**
	 * extract the gt(ground truth) results and put them into a
	 * 'AllResultCounter' class
	 * 
	 * @param truthFileName
	 *            The file that contains the true positive ranking of the
	 *            relevant results
	 * @return A class with all of the results
	 * @throws NumberFormatException
	 * @throws IOException
	 */
	private void getTrueRank(String truthFileName) {
		String line;
		try {
			// query id, docs id
			mTruthResultRank = new ResultSet();
			File truthFile = new File(truthFileName);
			FileInputStream fileInputStream = new FileInputStream(truthFile);
			InputStreamReader inputStreamReader = new InputStreamReader(
					fileInputStream);
			BufferedReader bufferedReader = new BufferedReader(
					inputStreamReader);

			while ((line = bufferedReader.readLine()) != null) {
				String[] formattedLine = line.split(" ", 4);
				int queryId = Integer.parseInt(formattedLine[0].trim());
				int docId = Integer.parseInt(formattedLine[2].trim());
				mTruthResultRank.AddResults(queryId, docId);
			}

			bufferedReader.close();

		} catch (Exception exception) {
			Log.error(TAG, exception, true);
		}
	}

}
